// src/parsers/universal/babelV2AstProcessor.ts
import * as t from '@babel/types';
import { v4 as uuidv4 } from 'uuid';
import {
    type UniversalFlowNode,
    type UniversalFlowEdge,
    type UniversalFlowNodeData,
    type NodeSemanticType,
    type SourceFormat
} from '@/models/universalFlow';
import {
    generateNodeCode,
    getIdentifierName,
    getLiteralValue,
    getSemanticTypeV2,
    getNodeLocation
} from './babelAstUtils';
import { babelService } from '@/services/babelService';

// Define the context passed during traversal
interface ProcessingContext {
    parentEvalPath: string | null; // Path of the parent in the evaluated structure
    currentKeyOrIndex: string | number | null; // Key or index relative to the parent
    level?: number; // <<< ADD level property (optional)
    order?: number; // <<< ADD order property (optional)
}

// --- Helper Functions are now correctly imported from babelAstUtils.ts ---
// Removed local getNodeLocation and getSemanticType definitions

export class BabelV2AstProcessor {
    private nodes: UniversalFlowNode[] = [];
    private edges: UniversalFlowEdge[] = [];
    private nodeMap: Map<t.Node, string> = new Map(); // Maps Babel Node -> Flow Node ID
    private format: SourceFormat;
    private ast: t.File;
    private declarationIdentifierMap: Map<string, t.VariableDeclarator> = new Map();
    private exportedValueRootNode: t.Node | null = null;

    constructor(ast: t.File, format: SourceFormat = 'javascript') {
        this.ast = ast;
        this.format = format;
    }

    private findExportedValueRootNode(programBody: t.Statement[]): t.Node | null {
        // Correct usage of find with type predicate
        const exportDefaultDeclaration = programBody.find(
            (stmt): stmt is t.ExportDefaultDeclaration => t.isExportDefaultDeclaration(stmt)
        );

        if (exportDefaultDeclaration) {
            if (t.isIdentifier(exportDefaultDeclaration.declaration)) {
                const exportName = exportDefaultDeclaration.declaration.name;
                console.log(`[Babel V2 Processor] Found export default identifier: ${exportName}`);
                const declaration = programBody.find(
                    (node): node is t.VariableDeclaration =>
                        t.isVariableDeclaration(node) &&
                        node.declarations.some(
                            (declarator) =>
                                t.isIdentifier(declarator.id) && declarator.id.name === exportName
                        )
                );
                if (declaration && declaration.declarations.length > 0 && declaration.declarations[0].init) {
                    console.log(`[Babel V2 Processor] Found exported identifier '${exportName}' pointing to root node type: ${declaration.declarations[0].init.type}`);
                    return declaration.declarations[0].init;
                } else {
                    console.warn(`[Babel V2 Processor] Variable declaration for exported identifier '${exportName}' not found or has no initializer.`);
                }
            } else if (t.isFunctionDeclaration(exportDefaultDeclaration.declaration)) {
                console.warn("[Babel V2 Processor] Exported function declaration found. Path calculation might be limited.");
                return exportDefaultDeclaration.declaration;
            } else if (t.isClassDeclaration(exportDefaultDeclaration.declaration)) {
                console.warn("[Babel V2 Processor] Exported class declaration found. Path calculation might be limited.");
                return exportDefaultDeclaration.declaration;
            } else if (t.isExpression(exportDefaultDeclaration.declaration)) {
                console.log(`[Babel V2 Processor] Found export default expression of type: ${exportDefaultDeclaration.declaration.type}`);
                return exportDefaultDeclaration.declaration;
            }
        } else {
            console.warn("[Babel V2 Processor] No 'export default' found. Path calculation might be limited.");
        }
        return null;
    }

    private buildDeclarationMap(programBody: t.Statement[]): void {
        this.declarationIdentifierMap.clear();
        for (const statement of programBody) {
            if (t.isVariableDeclaration(statement)) {
                for (const declarator of statement.declarations) {
                    if (t.isIdentifier(declarator.id)) {
                        this.declarationIdentifierMap.set(declarator.id.name, declarator);
                    }
                }
            }
        }
        console.log(`[Babel V2 Processor] Built declaration map with ${this.declarationIdentifierMap.size} entries.`);
    }

    processAst(): { nodes: UniversalFlowNode[]; edges: UniversalFlowEdge[]; rootId: string | null } {
        console.log('[Babel V2 Processor] Starting AST processing (Revised Entry Point)...');
        this.nodes = [];
        this.edges = [];
        this.nodeMap.clear();
        // Keep map for potential future use, but don't rely on it for path linking initially
        this.buildDeclarationMap(this.ast.program.body);

        // Find the root node to start traversal from
        this.exportedValueRootNode = this.findExportedValueRootNode(this.ast.program.body);

        let rootId: string | null = null;
        if (this.exportedValueRootNode) {
            console.log(`[Babel V2 Processor] Starting traversal from exported root node: ${this.exportedValueRootNode.type}`);
            // Start traversal from the identified export root with the base path context
            rootId = this.visitNode(this.exportedValueRootNode, null, { parentEvalPath: '', currentKeyOrIndex: null });
        } else {
            // Fallback: If no clear export found, traverse the whole program body,
            // but paths might not be meaningful relative to an "evaluated result".
            // Create a synthetic root node for the program.
            console.warn("[Babel V2 Processor] No exported value root found. Traversing program body. Paths may be limited.");
            const programNodeId = uuidv4();
            this.nodes.push({
                id: programNodeId,
                type: 'universal',
                position: { x: 0, y: 0 },
                data: {
                    name: '[Program Body]',
                    attributes: {},
                    metadata: { rawNodeType: 'Program' },
                    nodeSemanticType: 'root',
                    format: this.format,
                    sourceElement: null,
                    level: 0,
                    order: 0,
                    path: null, // Program itself has no path in the evaluated context
                    childrenIds: [],
                },
            });
            rootId = programNodeId;
            this.ast.program.body.forEach(stmt => {
                const childId = this.visitNode(stmt, rootId, { parentEvalPath: null, currentKeyOrIndex: null }); // Visit statements without path context
                // Link statement nodes to the synthetic program root
                const programNode = this.nodes.find(n => n.id === rootId);
                // Check if programNode and its data exist before accessing childrenIds
                if (programNode?.data && childId) {
                    programNode.data.childrenIds = programNode.data.childrenIds || [];
                    programNode.data.childrenIds.push(childId);
                }
            });
        }

        console.log(`[Babel V2 Processor] Processing finished. Nodes: ${this.nodes.length}, Edges: ${this.edges.length}, Root Node ID: ${rootId}`);
        // Remove the identifier path linking for now, as it seems to complicate things
        // this.linkIdentifierPaths(); // Disable this
        return { nodes: this.nodes, edges: this.edges, rootId };
    }

    // Helper to safely check and get literal value
    private getLiteralValue(node: t.Node): string | number | boolean | null | undefined | bigint | RegExp {
        if (t.isStringLiteral(node) || t.isNumericLiteral(node) || t.isBooleanLiteral(node)) {
            return node.value;
        }
        if (t.isBigIntLiteral(node)) {
            return BigInt(node.value); // Return as BigInt
        }
        if(t.isRegExpLiteral(node)) {
            // Reconstruct RegExp object
            try { return new RegExp(node.pattern, node.flags); }
            catch(e) { console.error("Error creating RegExp:", e); return undefined; }
        }
        // NullLiteral has no value property
        if (t.isNullLiteral(node)) return null;
        // Add other literal types if necessary (e.g., DecimalLiteral)
        return undefined;
    }


    private createFlowNode(babelNode: t.Node, parentId: string | null, context: ProcessingContext): string {
        const existingId = this.nodeMap.get(babelNode);
        if (existingId) {
            if (parentId) {
                this.createFlowEdge(parentId, existingId);
            }
            return existingId;
        }

        const flowNodeId = uuidv4();
        this.nodeMap.set(babelNode, flowNodeId);

        let currentEvalPath: string | null = null;
        const haveSameLocation = (nodeA: t.Node | null, nodeB: t.Node | null): boolean => {
            if (!nodeA || !nodeB || !nodeA.loc || !nodeB.loc || nodeA.start === null || nodeA.start === undefined || nodeB.start === null || nodeB.start === undefined || nodeA.end === null || nodeA.end === undefined || nodeB.end === null || nodeB.end === undefined) return false;
            return nodeA.start === nodeB.start && nodeA.end === nodeB.end;
        };

        // --- START DEBUG LOGGING ---
        const rootLoc = this.exportedValueRootNode?.loc;
        const nodeLoc = babelNode?.loc;
        const rootStart = rootLoc?.start;
        const rootEnd = rootLoc?.end;
        const nodeStart = nodeLoc?.start;
        const nodeEnd = nodeLoc?.end;
        console.log(`[DEBUG createFlowNode ${babelNode.type} ID:${flowNodeId}]`);
        console.log(`  > Root Node Loc: Start(${rootStart?.line}:${rootStart?.column}) End(${rootEnd?.line}:${rootEnd?.column})`);
        console.log(`  > Curr Node Loc: Start(${nodeStart?.line}:${nodeStart?.column}) End(${nodeEnd?.line}:${nodeEnd?.column})`);
        // --- END DEBUG LOGGING ---

        const isExportedRoot = this.exportedValueRootNode && haveSameLocation(babelNode, this.exportedValueRootNode);
        // --- ADD Log for isExportedRoot result ---
        console.log(`  > isExportedRoot check result: ${isExportedRoot}`);
        // --- END Log ---

        if (isExportedRoot) {
            currentEvalPath = '';
            console.log(`[Babel V2 Processor] Assigning root path ('') to node ID ${flowNodeId} based on location match for node type: ${babelNode.type}`);
        } else if (context.parentEvalPath !== null && context.currentKeyOrIndex !== null) {
            let pathSegment = '';
            if (typeof context.currentKeyOrIndex === 'number') {
                pathSegment = `[${context.currentKeyOrIndex}]`;
            } else if (typeof context.currentKeyOrIndex === 'string') {
                if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(context.currentKeyOrIndex)) {
                    pathSegment = context.parentEvalPath === '' ? context.currentKeyOrIndex : `.${context.currentKeyOrIndex}`;
                } else {
                    pathSegment = `["${context.currentKeyOrIndex.replace(/"/g, '\\"')}"]`;
                }
            }
            currentEvalPath = context.parentEvalPath + pathSegment;
        } else {
            // Path remains null
        }

        // --- Start: Directly populate base data ---
        const literalValue = this.getLiteralValue(babelNode); // Use helper
        const nodeName = getIdentifierName(babelNode as t.LVal) ||
            (literalValue !== undefined ? String(literalValue) : null) || // Use formatted literal value
            generateNodeCode(babelNode).substring(0, 50);

        const semanticType = getSemanticTypeV2(babelNode);
        const attributes: Record<string, string> = {};
        const content: string | undefined = literalValue !== undefined ? String(literalValue) : undefined;

        const metadata: Record<string, any> = {
            rawNodeType: babelNode.type,
            loc: getNodeLocation(babelNode), // Use local helper
        };
        if (t.isBinaryExpression(babelNode) || t.isLogicalExpression(babelNode)) {
            metadata.operator = babelNode.operator;
        } else if (t.isUnaryExpression(babelNode)) {
            metadata.operator = babelNode.operator;
            metadata.prefix = babelNode.prefix;
        } else if (t.isCallExpression(babelNode)) {
            metadata.functionName = generateNodeCode(babelNode.callee).substring(0, 30);
        } else if (t.isNewExpression(babelNode)) {
            metadata.constructorName = generateNodeCode(babelNode.callee).substring(0, 30);
        }
        // --- End: Directly populate base data ---

        // Calculate level safely
        let level = 0;
        if (parentId) {
            const parentNode = this.nodes.find(n => n.id === parentId);
            // Add null check for parentNode and parentNode.data
            level = (parentNode?.data?.level ?? -1) + 1; // Use -1 base if parent somehow not found, results in level 0
        }

        const flowNodeData: UniversalFlowNodeData = {
            name: nodeName,
            attributes: attributes,
            content: content,
            metadata: metadata,
            nodeSemanticType: semanticType,
            format: this.format,
            sourceElement: babelNode,
            level: context.level ?? 0, // <<< Use optional chaining/nullish coalescing
            order: context.order ?? 0, // <<< Use optional chaining/nullish coalescing
            path: currentEvalPath,
            value: literalValue,
            childrenIds: [],
        };

        // --- Start: Plan 1 - Add Extra Children for Dynamic Nodes ---
        const needsExtraChildren =
            // Ensure it has a calculated path (meaning it potentially maps to evaluatedConfig)
            currentEvalPath !== null &&
            // Exclude the absolute root node (path === '') itself
            currentEvalPath !== '' &&
            // Include relevant expression types
            (
                t.isIdentifier(babelNode) ||
                t.isCallExpression(babelNode) ||
                t.isMemberExpression(babelNode) ||
                t.isBinaryExpression(babelNode) ||
                t.isUnaryExpression(babelNode) ||
                t.isConditionalExpression(babelNode) ||
                t.isNewExpression(babelNode) ||
                t.isTemplateLiteral(babelNode) ||
                // Also include Literals if they have a path (might represent direct values in objects/arrays)
                t.isLiteral(babelNode) ||
                // Include Object/Array expressions if they have a path (intermediate structures)
                t.isObjectExpression(babelNode) ||
                t.isArrayExpression(babelNode)
            );

        let sourceSnippetChildId: string | null = null;
        let evaluatedResultChildId: string | null = null;

        if (needsExtraChildren) {
            // 1. Create Source Snippet Child
            try {
                const sourceSnippet = babelService.printAstNode(babelNode); // Use imported service

                sourceSnippetChildId = uuidv4();
                const snippetNodeData: UniversalFlowNodeData = {
                    name: 'Source Code',
                    content: sourceSnippet,
                    attributes: {},
                    metadata: { rawNodeType: 'InternalSourceSnippet' },
                    nodeSemanticType: 'sourceSnippet',
                    format: this.format,
                    sourceElement: null,
                    level: (context.level ?? 0) + 1, // <<< Use optional chaining/nullish coalescing
                    order: -2,
                    path: null,
                    childrenIds: [],
                };
                this.nodes.push({ id: sourceSnippetChildId, type: 'universal', position: { x: 0, y: 0 }, data: snippetNodeData });
                // REMOVE this.nodeMap.set(`snippet-${flowNodeId}`, sourceSnippetChildId);

            } catch (err) {
                console.error(`[Babel V2 Processor] Failed to generate source snippet for node ${flowNodeId}:`, err);
                sourceSnippetChildId = null;
            }

            // 2. Create Evaluated Result Placeholder Child
            evaluatedResultChildId = uuidv4();
            const resultNodeData: UniversalFlowNodeData = {
                name: 'Evaluated Result',
                content: '[Pending...]\n(Requires QuickJS Execution)',
                attributes: {},
                metadata: { rawNodeType: 'InternalEvaluatedResult' },
                nodeSemanticType: 'evaluatedResult',
                format: this.format,
                sourceElement: null,
                level: (context.level ?? 0) + 1, // <<< Use optional chaining/nullish coalescing
                order: -1,
                path: null,
                childrenIds: [],
            };
            this.nodes.push({ id: evaluatedResultChildId, type: 'universal', position: { x: 0, y: 0 }, data: resultNodeData });
            // REMOVE this.nodeMap.set(`result-${flowNodeId}`, evaluatedResultChildId);

            // Add IDs to parent's children list
            if (sourceSnippetChildId) {
                flowNodeData.childrenIds?.push(sourceSnippetChildId);
            }
            if (evaluatedResultChildId) {
                flowNodeData.childrenIds?.push(evaluatedResultChildId);
            }
            console.log(`[Babel V2 Processor] Added source/result children for dynamic node ${flowNodeId} (${babelNode.type})`);
        }
        // --- End: Plan 1 ---

        // --- Add Node and Edges ---
        this.nodes.push({ id: flowNodeId, type: 'universal', position: { x: 0, y: 0 }, data: flowNodeData });
        if (parentId) {
            this.createFlowEdge(parentId, flowNodeId);
            // Link the extra children to the parent as well
            // Note: createFlowEdge handles duplicates, but we call it directly here
            if (sourceSnippetChildId) {
                this.edges.push({
                    id: `e-${parentId}-${sourceSnippetChildId}`,
                    source: flowNodeId, // Parent node is the source
                    target: sourceSnippetChildId,
                    data: { relationshipType: 'computational-detail' }
                });
            }
            if (evaluatedResultChildId) {
                this.edges.push({
                    id: `e-${parentId}-${evaluatedResultChildId}`,
                    source: flowNodeId, // Parent node is the source
                    target: evaluatedResultChildId,
                    data: { relationshipType: 'computational-detail' }
                });
            }
        }

        return flowNodeId;
    }

    private createFlowEdge(sourceId: string, targetId: string): void {
        const edgeId = `e-${sourceId}-${targetId}`;
        if (!this.edges.some(e => e.id === edgeId)) {
            const edge: UniversalFlowEdge = {
                id: edgeId,
                source: sourceId,
                target: targetId,
                data: { relationshipType: 'parent-child' },
            };
            this.edges.push(edge);
        }
    }

    private visitNode(babelNode: t.Node | null | undefined, parentId: string | null, context: ProcessingContext): string | null {
        if (!babelNode) return null;

        const flowNodeId = this.createFlowNode(babelNode, parentId, context);
        const flowNode = this.nodes.find(n => n.id === flowNodeId);
        // Add null check for flowNode and flowNode.data
        if (!flowNode?.data) return flowNodeId; // Exit early if node or data is missing

        const visitChild = (childNode: t.Node | null | undefined, childContextOverrides: Partial<ProcessingContext> = {}) => {
            if (!childNode) return;
            // Use flowNode.data safely here
            const newContext: ProcessingContext = {
                parentEvalPath: flowNode.data!.path, // Use non-null assertion as we checked above
                currentKeyOrIndex: null,
                ...childContextOverrides,
            };
            this.visitNode(childNode, flowNodeId, newContext);
        };

        // --- Recursive Traversal Logic based on Node Type ---
        if (t.isProgram(babelNode)) {
            babelNode.body.forEach(stmt => this.visitNode(stmt, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isExportDefaultDeclaration(babelNode)) {
            this.visitNode(babelNode.declaration, flowNodeId, { parentEvalPath: '', currentKeyOrIndex: null });
        } else if (t.isVariableDeclaration(babelNode)) {
            babelNode.declarations.forEach(decl => this.visitNode(decl, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isVariableDeclarator(babelNode)) {
            this.visitNode(babelNode.id, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.init, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isObjectExpression(babelNode)) {
            const nodeId = this.createFlowNode(babelNode, parentId, context);
            const node = this.nodes.find(n => n.id === nodeId);
            const basePath = node?.data?.path; // Get path calculated by createFlowNode for this ObjectExpression

            if (basePath !== null && basePath !== undefined) { // Check if basePath is valid
                babelNode.properties.forEach((prop) => {
                    if (prop.type === 'ObjectProperty') {
                        let keyName: string | null = null;
                        if (prop.key.type === 'Identifier') {
                            keyName = prop.key.name;
                        } else if (prop.key.type === 'StringLiteral' || prop.key.type === 'NumericLiteral') {
                            keyName = String(prop.key.value);
                        }
                        // Visit the value node, passing the ObjectExpression's path as parentEvalPath
                        // and the property key as currentKeyOrIndex.
                        if (keyName !== null) {
                            this.visitNode(prop.value as t.Node, nodeId, { parentEvalPath: basePath, currentKeyOrIndex: keyName });
                        } else {
                            // Handle computed/complex keys - visit value without passing path info
                            this.visitNode(prop.value as t.Node, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                        }
                    } else if (prop.type === 'ObjectMethod') {
                        let keyName: string | null = null;
                        if (prop.key.type === 'Identifier') {
                            keyName = prop.key.name;
                        } else if (prop.key.type === 'StringLiteral' || prop.key.type === 'NumericLiteral') {
                            keyName = String(prop.key.value);
                        }
                        // Visit the method body, passing correct context
                        if (keyName !== null) {
                            this.visitNode(prop.body, nodeId, { parentEvalPath: basePath, currentKeyOrIndex: keyName });
                        } else {
                            this.visitNode(prop.body, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                        }
                        // Also visit params if needed, usually path null
                        // prop.params.forEach(p => this.visitNode(p, nodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
                    } else if (prop.type === 'SpreadElement') {
                        // Visit spread element, path calculation difficult
                        this.visitNode(prop.argument, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                    }
                });
            } else {
                // If the ObjectExpression itself doesn't have a valid path, visit children without path context
                babelNode.properties.forEach((prop) => {
                    if (prop.type === 'ObjectProperty') this.visitNode(prop.value as t.Node, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                    else if (prop.type === 'ObjectMethod') this.visitNode(prop.body, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                    else if (prop.type === 'SpreadElement') this.visitNode(prop.argument, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                });
            }
            return nodeId; // Return the ObjectExpression node ID
        } else if (t.isArrayExpression(babelNode)) {
            const nodeId = this.createFlowNode(babelNode, parentId, context);
            const node = this.nodes.find(n => n.id === nodeId);
            const basePath = node?.data?.path; // Get path calculated for this ArrayExpression

            if (basePath !== null && basePath !== undefined) { // Check if basePath is valid
                babelNode.elements.forEach((element, index) => {
                    if (element) { // Handle potential null elements (sparse arrays)
                        // Visit the element, passing ArrayExpression's path as parentEvalPath
                        // and the element index as currentKeyOrIndex.
                        this.visitNode(element, nodeId, { parentEvalPath: basePath, currentKeyOrIndex: index });
                    }
                });
            } else {
                // If ArrayExpression has no valid path, visit children without path context
                babelNode.elements.forEach(element => {
                    if (element) {
                        this.visitNode(element, nodeId, { parentEvalPath: null, currentKeyOrIndex: null });
                    }
                });
            }
            return nodeId; // Return the ArrayExpression node ID
        } else if (t.isFunctionDeclaration(babelNode) || t.isFunctionExpression(babelNode) || t.isArrowFunctionExpression(babelNode)) {
            if ('id' in babelNode && babelNode.id) {
                this.visitNode(babelNode.id, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            }
            babelNode.params.forEach(param => this.visitNode(param, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
            this.visitNode(babelNode.body, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isBlockStatement(babelNode)) {
            babelNode.body.forEach(stmt => this.visitNode(stmt, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isExpressionStatement(babelNode)) {
            this.visitNode(babelNode.expression, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isCallExpression(babelNode)) {
            this.visitNode(babelNode.callee, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            babelNode.arguments.forEach(arg => this.visitNode(arg, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isMemberExpression(babelNode)) {
            this.visitNode(babelNode.object, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.property, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isBinaryExpression(babelNode) || t.isLogicalExpression(babelNode)) {
            this.visitNode(babelNode.left, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.right, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isUnaryExpression(babelNode)) {
            this.visitNode(babelNode.argument, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isConditionalExpression(babelNode)) {
            this.visitNode(babelNode.test, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.consequent, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.alternate, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isReturnStatement(babelNode)) {
            this.visitNode(babelNode.argument, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isIfStatement(babelNode)) {
            this.visitNode(babelNode.test, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.consequent, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.alternate, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isForStatement(babelNode)) {
            this.visitNode(babelNode.init, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.test, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.update, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.body, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isForInStatement(babelNode) || t.isForOfStatement(babelNode)) {
            this.visitNode(babelNode.left, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.right, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.body, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isWhileStatement(babelNode) || t.isDoWhileStatement(babelNode)) {
            this.visitNode(babelNode.test, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.body, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isSwitchStatement(babelNode)) {
            this.visitNode(babelNode.discriminant, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            babelNode.cases.forEach(switchCase => this.visitNode(switchCase, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isSwitchCase(babelNode)) {
            this.visitNode(babelNode.test, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            babelNode.consequent.forEach(stmt => this.visitNode(stmt, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isTryStatement(babelNode)) {
            this.visitNode(babelNode.block, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.handler, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.finalizer, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isCatchClause(babelNode)) {
            this.visitNode(babelNode.param, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.body, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isThrowStatement(babelNode)) {
            this.visitNode(babelNode.argument, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isNewExpression(babelNode)) {
            this.visitNode(babelNode.callee, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            babelNode.arguments.forEach(arg => this.visitNode(arg, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isAssignmentExpression(babelNode)) {
            this.visitNode(babelNode.left, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.right, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isUpdateExpression(babelNode)) {
            this.visitNode(babelNode.argument, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isTemplateLiteral(babelNode)) {
            babelNode.quasis.forEach(quasi => this.visitNode(quasi, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
            babelNode.expressions.forEach(expr => this.visitNode(expr, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null }));
        } else if (t.isTaggedTemplateExpression(babelNode)) {
            this.visitNode(babelNode.tag, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
            this.visitNode(babelNode.quasi, flowNodeId, { parentEvalPath: null, currentKeyOrIndex: null });
        } else if (t.isIdentifier(babelNode)) {
            const currentPath = flowNode.data.path;
            if (currentPath !== null) {
                const identifierName = babelNode.name;
                const originalDeclarator = this.declarationIdentifierMap.get(identifierName);
                if (originalDeclarator) {
                    const originalNodeId = this.nodeMap.get(originalDeclarator);
                    if (originalNodeId) {
                        const originalFlowNode = this.nodes.find(n => n.id === originalNodeId);
                        if (originalFlowNode?.data && originalFlowNode.data.path === null) {
                            console.log(`[Babel V2 Processor] Linking path '${currentPath}' back to declaration node '${identifierName}' (ID: ${originalNodeId})`);
                            originalFlowNode.data.path = currentPath;
                        }
                    }
                }
            }
        } else if (t.isLiteral(babelNode)) {
            // Literals get their path from context when visited via ObjectProperty/ArrayExpression.
            // No specific child visiting needed.
        }

        return flowNodeId;
    }
}